/**************************************************************************************
*  File.c
*
*  This file defines all native methods that have to be implemented to support
*  java/lang/File class.
*  Actual implementation is target device dependent and therefore must be
*  implemented by the application developer.
*
*  Application developer should use the following functions to get access to
*  arrays and String object:
*       vmGetStringCount
*       vmGetStringArray
*       vmGetArraySize
*       vmGetArray
*  These functions are accessible via vm_config_t structure (see Win32 demo
*  application for examples on how to use them).
*
*  Native methods implementation
*  -----------------------------
*  Note that if the native method is NOT declared as static then param[0]
*  contains object's reference and must be skiped when accessing the native
*  method parameters.
**************************************************************************************
*
* This file is covered by the GNU GPL with the following exception:
*   As a special exception, the copyright holders of this library give you permission
*   to link this library with independent modules to produce an executable, regardless
*   of the license terms of these independent modules, and to copy and distribute the
*   resulting executable under terms of your choice, provided that you also meet, for
*   each linked independent module, the terms and conditions of the license of that
*   module. An independent module is a module which is not derived from or based on
*   this library. If you modify this library, you may extend this exception to your
*   version of the library, but you are not obligated to do so. If you do not wish
*   to do so, delete this exception statement from your version.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL RTJ COMPUTING BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Copyright (c) 2000-2002 RTJ Computing Pty. Ltd. All rights reserved.
**************************************************************************************/
#include <dos.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <dir.h>
#include <io.h>
#include <dos.h>

#include "javavm.h"

extern vm_config_t vmconfig;

/****************************************************************************
** Retrieves file name from object reference.
** On entry     ref  - reference to VM object containing the file name
** On exit      name - pointer to buffer that receives the file name
**              len  - length of the name buffer
** Function will return the actual number of bytes copied into name buffer.
****************************************************************************/
jint getName(int32 ref, char *name, int len)
{
    jint count;
    int i;
    jchar *arr;

    count = (int)vmconfig.vmGetStringCount(ref);
    if (count >= len)
        count = len - 1;
    if (count > 0)
    {
        arr = vmconfig.vmGetStringArray(ref);
        for (i=0; i < count; i++)
            name[i] = (char)arr[i];
        name[i] = 0;
    }
    return count;
}

/****************************************************************************
** Returns the length of a file specified by the handle.
****************************************************************************/
int32 getFileLength0(int32 handle)
{
    return (int32)filelength((uint16)handle);
}
/****************************************************************************
** Reads the content of the file.
** param[1] - file handle
** param[2] - reference to destination byte array
** param[3] - offset to destination array
** param[4] - number of bytes to read (copy to dest. array)
** Returns 0 if failed, >= 0 number of bytes read.
****************************************************************************/
int32 read0(int32 param[])
{
    uint16 nread;
    jint count;
    uint8 huge *buf;

    count = vmconfig.vmGetArraySize(param[2]);
    if (count > 0 && (param[3] + param[4]) <= count)
    {
        buf = (uint8 huge *)vmconfig.vmGetArray(param[2]);
        buf += (uint16)param[3];
// TODO: use while loop to read bytes in case length (param[4]) is
// greater than 0xFFFF (not very likely on small systems)
        nread = read((uint16)param[1], buf, (uint16)param[4]);
        if (nread > 0)
            return nread;
    }
    return -1;
}
/****************************************************************************
** Reads the content of the file.
** param[1] - native file handle.
** param[2] - reference to source byte array
** param[3] - offset to source array
** param[4] - number of bytes to write (copy from source array)
** Returns -1 if failed, 0 if successful.
****************************************************************************/
int32 write0(int32 param[])
{
    uint8 huge *buf;
    jint count;

    count = vmconfig.vmGetArraySize(param[2]);
    if (count > 0)
    {
        buf = (uint8 huge *)vmconfig.vmGetArray(param[2]);
        buf += param[3];
// TODO: use while loop to write bytes in case length (param[4]) is
// greater than 0xFFFF (not very likely on small systems)
        return (write((int16)param[1], buf, (uint16)param[4]) >= 0) ? 0 : -1;
    }
    return 0;
}
/****************************************************************************/



/****************************************************************************
* Checks is it can be read from this file by checking the access attributes.
* If underlying file system doesn't implement access attributes then this function
* should always return true.
* On entry param[1] will contain reference to String with path to a file.
*
* Java prototype:
*           native private boolean canRead0(String path);
*
****************************************************************************/
#pragma argsused
int16 java_io_File_canRead0(int32 param[], int32 *retval)
{
    /* Add your code here if required */

    *retval = true;
    return NO_EXCEP;
}


/****************************************************************************
* Checks is it can be written to this file by checking the access attributes.
* If underlying file system doesn't implement access attributes then this function
* should always return true.
* On entry param[1] will contain reference to String with path to a file.
*
* Java prototype:
*           native private boolean canWrite0(String path);
*
****************************************************************************/
#pragma argsused
int16 java_io_File_canWrite0(int32 param[], int32 *retval)
{
    /* Add your code here if required */

    *retval = true;
    return NO_EXCEP;
}


/****************************************************************************
* Deletes a file.
* On entry param[1] will contain reference to String with path to a file.
* On return function should indicate whether deletetion was successfull via
* retval parameter.
*
* Java prototype:
*           native private boolean delete0(String path);
*
****************************************************************************/
int16 java_io_File_delete0(int32 param[], int32 *retval)
{
    char name[128];

    *retval = false;
    if (getName(param[1], name, sizeof(name)) > 0)
        *retval = (unlink(name) == 0);

    return NO_EXCEP;
}


/****************************************************************************
* Deletes a directory.
* On entry param[1] will contain reference to String with path to a directory.
* On return function should indicate whether directory was removed via
* retval parameter.
* If underlying file system doesn't implement directories then this function
* should always return true.
*
* Java prototype:
*           native private boolean rmdir0(String path);
*
****************************************************************************/
int16 java_io_File_rmdir0(int32 param[], int32 *retval)
{
    char name[128];

    *retval = false;
    if (getName(param[1], name, sizeof(name)) > 0)
        *retval = (rmdir(name) == 0);
    return NO_EXCEP;
}


/****************************************************************************
* Checks if the file exists.
* On entry param[1] will contain reference to String with path to a file.
* On return function should indicate whether file exists via retval parameter.
*
* Java prototype:
*           native private boolean exists0(String path);
*
****************************************************************************/
int16 java_io_File_exists0(int32 param[], int32 *retval)
{
    int16 handle;
    char name[128];

    *retval = false;
    if (getName(param[1], name, sizeof(name)) > 0)
    {
        handle = open(name, O_RDONLY);
        if (handle >= 0)
        {
            close(handle);
            *retval = true;
        }
    }

    return NO_EXCEP;
}


/****************************************************************************
* Checks if item is a directory.
* On entry param[1] will contain reference to String with path to an item.
* On return function should indicate whether item is directory via
* retval parameter.
* If underlying file system doesn't implement directories then this function
* should always return false.
*
* Java prototype:
*           native private boolean isDir0(String path);
*
****************************************************************************/
int16 java_io_File_isDir0(int32 param[], int32 *retval)
{
    unsigned attr;
    char name[128];

    *retval = false;
    if (getName(param[1], name, sizeof(name)) > 0)
    {
        if (_dos_getfileattr(name, &attr) == 0)
            *retval = (attr & _A_SUBDIR) ? true : false;
    }

    return NO_EXCEP;
}


/****************************************************************************
* Checks if item is a file.
* On entry param[1] will contain reference to String with path to an item.
* On return function should indicate whether item is file via retval parameter.
*
* Java prototype:
*           native private boolean isFile0(String path);
*
****************************************************************************/
int16 java_io_File_isFile0(int32 param[], int32 *retval)
{
    unsigned attr;
    char name[128];

    *retval = false;
    if (getName(param[1], name, sizeof(name)) > 0)
    {
        if (_dos_getfileattr(name, &attr) == 0)
            *retval = (attr & _A_SUBDIR) ? false : true;
    }

    return NO_EXCEP;
}


/****************************************************************************
* Get last time modified.
* On entry param[1] will contain reference to String with path to a file.
* Function should return (via retval parameter) time as 32-bit time value.
* If underlying file system doesn't implement file time stamping then this function
* should always return zero.
*
* Java prototype:
*           native private int lastModified0(String path);
*
****************************************************************************/
#pragma argsused
int16 java_io_File_lastModified0(int32 param[], int32 *retval)
{
    /* Add your code here if required */

    *retval = 0;
    return NO_EXCEP;
}


/****************************************************************************
* Retrieves the size of a file.
* On entry param[1] will contain reference to String with path to a file.
* On return function should provide file size via retval parameter.
*
* Java prototype:
*           native private int length0(String path);
*
****************************************************************************/
int16 java_io_File_length0(int32 param[], int32 *retval)
{
    char name[128];
    struct find_t ffblk;

    *retval = 0;
    if (getName(param[1], name, sizeof(name)) > 0)
    {
        if (_dos_findfirst(name, _A_NORMAL, &ffblk) == 0)
            *retval = ffblk.size;
    }
    return NO_EXCEP;
}


/****************************************************************************
* Creates a directory.
* On entry param[1] will contain reference to String with path to a directory.
* On return function should indicate whether directory was created via
* retval parameter.
* If underlying file system doesn't implement access attributes then this function
* should always return false.
*
* Java prototype:
*           native private boolean mkdir0(String path);
*
****************************************************************************/
int16 java_io_File_mkdir0(int32 param[], int32 *retval)
{
    char name[128];

    *retval = false;
    if (getName(param[1], name, sizeof(name)) > 0)
        *retval = (mkdir(name) == 0);
    return NO_EXCEP;
}


/****************************************************************************
* Renames source file to destination.
* On entry param[1] will contain reference to String with path to a source file.
*          param[2] will contain reference to String with path to a dest file.
* On return function should indicate whether source file was renamed via
* retval parameter.
*
* Java prototype:
*           native private boolean renameTo0(String source, String dest);
*
****************************************************************************/
int16 java_io_File_renameTo0(int32 param[], int32 *retval)
{
    char nameFrom[128], nameTo[128];

    *retval = false;
    if (getName(param[1], nameFrom, sizeof(nameFrom)) > 0 &&
        getName(param[2], nameTo, sizeof(nameTo)) > 0)
    {
        *retval = (rename(nameFrom, nameTo) == 0);
    }

    return NO_EXCEP;
}


/****************************************************************************
* Indicates max number of characters that file or directory name can contain.
* On return function should provide max name size via retval parameter.
*
* Java prototype:
*           native private int maxNameLen0();
*
****************************************************************************/
#pragma argsused
int16 java_io_File_maxNameLen0(int32 param[], int32 *retval)
{
    /* Change to whatever is appropriate for implemented file system */
    *retval = 12;
    return NO_EXCEP;
}


/****************************************************************************
* Retrieves the number of files/dirs in the specified path.
* On entry param[1] will contain reference to String with path to a directory.
* On return function should indicate the count of files/dirs via retval parameter.
* If underlying file system doesn't implement directories then param[1] can
* be ignored.
*
* Java prototype:
*           native private int getDirItemCount0(String path);
*
****************************************************************************/
int16 java_io_File_getDirItemCount0(int32 param[], int32 *retval)
{
    char name[128];
    struct find_t ffblk;

    *retval = 0;
    if (getName(param[1], name, sizeof(name)) > 0)
    {
        if (name[strlen(name)-1] != '\\')
            strcat(name, "\\");
        strcat(name, "*.*");
        if (_dos_findfirst(name, _A_NORMAL, &ffblk) == 0)
        {
            (*retval)++;
            while (_dos_findnext(&ffblk) == 0)
                (*retval)++;
        }
    }

    return NO_EXCEP;
}


/****************************************************************************
* Retrieves the name of file/directory at specified index and copies the name
* into provided char array.
* On entry param[1] will contain reference to String with path to a directory.
*          param[2] will contain index of file/dir for to retrieve name
*          param[3] will contain reference to char array where file/dir name is
*                   to be copied
* On return function should indicate the name length of file/dir (number of chars
* copied into char array.
* If underlying file system doesn't implement directories then param[1] can
* be ignored.
*
* Java prototype:
*           native private int getDirItem0(String path, int index, char[] name);
*
****************************************************************************/
int16 java_io_File_getDirItem0(int32 param[], int32 *retval)
{
    char name[128];
    struct find_t ffblk;
    int32 idx = 0;
    jint count;
    jchar *arr;
    uint16 i;

    *retval = 0;
    if (getName(param[1], name, sizeof(name)) > 0)
    {
        if (name[strlen(name)-1] != '\\')
            strcat(name, "\\");
        strcat(name, "*.*");
        if (_dos_findfirst(name, _A_NORMAL, &ffblk) == 0)
        {
            if (idx != param[2])
            {
                idx++;
                while (_dos_findnext(&ffblk) == 0)
                {
                    if (idx == param[2])
                        break;
                    idx++;
                }
            }
            count = vmconfig.vmGetArraySize(param[3]);
            if (count > 0)
            {
                arr = (jchar *)vmconfig.vmGetArray(param[3]);
                *retval = strlen(ffblk.name);
                for (i=0; i < count && i < *retval; i++)
                    arr[i] = (jchar)ffblk.name[i];
            }
        }
    }
    return NO_EXCEP;
}

